﻿namespace ImageProcessing
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading;

    /// <summary>
    /// Class used for quantization functions and applications.
    /// </summary>
    public class MaskBits
    {
        /// <summary>
        /// Removes the bits.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="red">The red bool list.</param>
        /// <param name="green">The green bool list.</param>
        /// <param name="blue">The blue bool list.</param>
        /// <returns>
        /// Image with removed bits
        /// </returns>
        public static ImageStructure[,] RemoveBits(ImageStructure[,] originalImage, char[] red, char[] green, char[] blue)
        {
            string redMask = string.Empty;
            string greenMask = string.Empty;
            string blueMask = string.Empty;
            for (int i = 7; i >= 0; i--)
            {
                redMask += red[i];
                greenMask += green[i];
                blueMask += blue[i];
            }

            return ApplyMask(originalImage, Convert.ToByte(redMask, 2), Convert.ToByte(greenMask, 2), Convert.ToByte(blueMask, 2), false, false);
        }

        /// <summary>
        /// Restores the bits.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="imageToRestore">The image to restore.</param>
        /// <param name="red">The red.</param>
        /// <param name="green">The green.</param>
        /// <param name="blue">The blue.</param>
        /// <returns>
        /// Image with restored bits.
        /// </returns>
        public static ImageStructure[,] RestoreBits(ImageStructure[,] originalImage, ImageStructure[,] imageToRestore, char[] red, char[] green, char[] blue)
        {
            string redMask = string.Empty;
            string greenMask = string.Empty;
            string blueMask = string.Empty;
            for (int i = 7; i >= 0; i--)
            {
                redMask += red[i];
                greenMask += green[i];
                blueMask += blue[i];
            }

            // return ApplyMask(originalImage, imageToRestore, Convert.ToByte(redMask, 2), Convert.ToByte(greenMask, 2), Convert.ToByte(blueMask, 2));
            return imageToRestore;
        }

        /// <summary>
        /// Displays the bit.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="bitIndex">Index of the bit.</param>
        /// <param name="colorChar">The color char.</param>
        /// <param name="greyScale">if set to <c>true</c> [grey scale].</param>
        /// <returns>
        /// Image with removed bits.
        /// </returns>
        public static ImageStructure[,] DisplayBit(ImageStructure[,] originalImage, int bitIndex, char colorChar, bool greyScale)
        {
            byte mask = 0;
            switch (bitIndex)
            {
                case 0:
                    mask = Convert.ToByte("00000001", 2);
                    break;
                case 1:
                    mask = Convert.ToByte("00000010", 2);
                    break;
                case 2:
                    mask = Convert.ToByte("00000100", 2);
                    break;
                case 3:
                    mask = Convert.ToByte("00001000", 2);
                    break;
                case 4:
                    mask = Convert.ToByte("00010000", 2);
                    break;
                case 5:
                    mask = Convert.ToByte("00100000", 2);
                    break;
                case 6:
                    mask = Convert.ToByte("01000000", 2);
                    break;
                case 7:
                    mask = Convert.ToByte("10000000", 2);
                    break;
            }

            switch (colorChar)
            {
                case 'R':
                    return ApplyMask(originalImage, mask, 0, 0, true, greyScale);
                case 'G':
                    return ApplyMask(originalImage, 0, mask, 0, true, greyScale);
                case 'B':
                    return ApplyMask(originalImage, 0, 0, mask, true, greyScale);
            }

            return originalImage;
        }

        /// <summary>
        /// Removes the bits.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="redMask">The red mask.</param>
        /// <param name="greenMask">The green mask.</param>
        /// <param name="blueMask">The blue mask.</param>
        /// <param name="isDisplay">if set to <c>true</c> [is display].</param>
        /// <param name="greyScale">if set to <c>true</c> [grey scale].</param>
        /// <returns>
        /// Image with mask applied to it.
        /// </returns>
        private static ImageStructure[,] ApplyMask(ImageStructure[,] originalImage, byte redMask, byte greenMask, byte blueMask, bool isDisplay, bool greyScale)
        {
            int height = originalImage.GetLength(0);
            int width = originalImage.GetLength(1);
            ImageStructure[,] modifiedImage = new ImageStructure[height, width];
            int rowsPerThread = height / Environment.ProcessorCount;
            int minHeight, maxHeight;
            Semaphore s = new Semaphore(1, 1000);
            Thread t = null;
            List<Thread> threadList = new List<Thread>();
            for (int i = 1; i < Environment.ProcessorCount; i++)
            {
                s.WaitOne();
                minHeight = i * rowsPerThread;
                maxHeight = (i + 1) * rowsPerThread;
                if (i == (Environment.ProcessorCount - 1))
                {
                    t = new Thread(delegate() { new MaskBits().ApplyMaskThreading(s, minHeight, height, originalImage, modifiedImage, width, redMask, greenMask, blueMask, isDisplay, greyScale); });
                    t.Start();
                    threadList.Add(t);
                    break;
                }

                t = new Thread(delegate() { new MaskBits().ApplyMaskThreading(s, minHeight, height, originalImage, modifiedImage, width, redMask, greenMask, blueMask, isDisplay, greyScale); });
                t.Start();
                threadList.Add(t);
            }
            
            new MaskBits().ApplyMaskThreading(s, 0, rowsPerThread, originalImage, modifiedImage, width, redMask, greenMask, blueMask, isDisplay, greyScale);
            foreach (Thread thread in threadList)
            {
                thread.Join();
            }

            return modifiedImage;
        }

        /// <summary>
        /// Threading function for mask apply.
        /// </summary>
        /// <param name="s">The semaphore.</param>
        /// <param name="minHeight">Minimum height to start from.</param>
        /// <param name="maxHeight">Max height to stop at.</param>
        /// <param name="originalImage">The original image.</param>
        /// <param name="modifiedImage">The modified image.</param>
        /// <param name="width">The width.</param>
        /// <param name="redMask">The red mask.</param>
        /// <param name="greenMask">The green mask.</param>
        /// <param name="blueMask">The blue mask.</param>
        /// <param name="isDisplay">if set to <c>true</c> [is display].</param>
        /// <param name="greyScale">if set to <c>true</c> [grey scale].</param>
        private void ApplyMaskThreading(Semaphore s, int minHeight, int maxHeight, ImageStructure[,] originalImage, ImageStructure[,] modifiedImage, int width, byte redMask, byte greenMask, byte blueMask, bool isDisplay, bool greyScale)
        {
            s.Release();
            int greyValue;
            for (int i = minHeight; i < maxHeight; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    modifiedImage[i, j].R = (byte)(originalImage[i, j].R & redMask);
                    modifiedImage[i, j].G = (byte)(originalImage[i, j].G & greenMask);
                    modifiedImage[i, j].B = (byte)(originalImage[i, j].B & blueMask);

                    if (isDisplay)
                    {
                        if (!greyScale)
                        {
                            if (modifiedImage[i, j].R == 0 && modifiedImage[i, j].G == 0 && modifiedImage[i, j].B == 0)
                            {
                                modifiedImage[i, j].R = modifiedImage[i, j].G = modifiedImage[i, j].B = 255;
                            }
                            else if (modifiedImage[i, j].R == 0 && modifiedImage[i, j].G == 0)
                            {
                                modifiedImage[i, j].B = 255;
                            }
                            else if (modifiedImage[i, j].R == 0 && modifiedImage[i, j].B == 0)
                            {
                                modifiedImage[i, j].G = 255;
                            }
                            else if (modifiedImage[i, j].B == 0 && modifiedImage[i, j].G == 0)
                            {
                                modifiedImage[i, j].R = 255;
                            }
                        }
                        else
                        {
                            greyValue = (modifiedImage[i, j].R + modifiedImage[i, j].G + modifiedImage[i, j].B) / 3;
                            if (greyValue == 0)
                            {
                                modifiedImage[i, j].R = modifiedImage[i, j].G = modifiedImage[i, j].B = 255;
                            }
                            else
                            {
                                modifiedImage[i, j].R = modifiedImage[i, j].G = modifiedImage[i, j].B = (byte)greyValue;
                            }
                        }
                    }
                }
            }
        }
    }
}